package com.hobee.basic.config.datasource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;


/**
 * 数据源的切换要在数据库事务之前, 设置AOP执行顺序(需要在事务之前，否则事务只发生在默认库中, 数值越小等级越高)
 *
 * @author junjie.li
 * @date 2020/6/29 19:12
 */
@Aspect
@Order(value = 1)
@Component
public class DataSourceAspect {

    private static Logger LOGGER = LoggerFactory.getLogger(DataSourceAspect.class);

    /**
     * 默认走写库方法名前缀
     */
    private static final String[] MASTER_ARR = new String[]{"add", "save", "insert", "update", "modify", "delete", "remove"};

    /**
     * 默认走读库方法名前缀
     */
    private static final String[] SLAVE_ARR = new String[]{"query", "find", "select", "get"};

    @Pointcut("execution(* com.hobee.basic.service.*.*(..)))")
    public void aspect() {
    }

    @Before("aspect()")
    private void before(JoinPoint point) {
        Object target = point.getTarget();
        String method = point.getSignature().getName();
        Class<?> clazz = target.getClass();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
        try {
            Method m = clazz.getMethod(method, parameterTypes);
            if (m.isAnnotationPresent(DataSourceAnnotation.class)) {
                DataSourceAnnotation data = m.getAnnotation(DataSourceAnnotation.class);
                DataSourceEnum value = data.value();
                DataSourceContextHolder.putDataSource(value.getCodeName());
                LOGGER.debug("-----------切换数据源, 数据源上下文实际赋值-----:{}", DataSourceContextHolder.getCurrentDataSource());
            } else {
                DataSourceContextHolder.putDataSource(getDefaultDataSourceByMethodName(m.getName()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据方法名默认获取数据源名称
     *
     * @param methodName 方法名
     * @return DataSourceName
     */
    private String getDefaultDataSourceByMethodName(String methodName) {
        for (String prefix : MASTER_ARR) {
            if (methodName.startsWith(prefix)) {
                return DataSourceEnum.MASTER.getCodeName();
            }
        }
        for (String prefix : SLAVE_ARR) {
            if (methodName.startsWith(prefix)) {
                return DataSourceEnum.SLAVE.getCodeName();
            }
        }
        return DataSourceEnum.SLAVE.getCodeName();
    }

    /**
     * 切面结束, 重置线程变量
     *
     * @param joinPoint 切点
     */
    @After("aspect()")
    public void after(JoinPoint joinPoint) {
        DataSourceContextHolder.removeCurrentDataSource();
        LOGGER.debug("重置数据源: Restore DataSource to [{}] in Method [{}]", DataSourceContextHolder.getCurrentDataSource(), joinPoint.getSignature());
    }
}
